package com.iwhalecloud.citybrain.flow.platform.manager.application.common.context;


import com.alibaba.fastjson.JSONObject;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.FlowNodeCandidateServiceAdapter;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.NodeCandidateService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.ResolveCandidateService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.ExpressionUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.SpringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowParticipantInfoVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.ModelNextNodeCandidateSettings;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.User;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.userentity.UserInfo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowNextNodeCandidateRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowParticipantRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.UserRepository;
import org.apache.commons.collections4.CollectionUtils;
import org.flowable.engine.common.impl.el.ExpressionManager;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.impl.delegate.FlowableCollectionHandler;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Handler - 处理会签时常量集合
 *
 * @author lilh
 * @date 2019-03-04 14:11
 */
@Component
public class ConstantCollectionHandler implements FlowableCollectionHandler {
    private static final long serialVersionUID = 5958870384439065351L;

    private Logger logger = LoggerFactory.getLogger(this.getClass());


    @Override
    public Collection resolveCollection(Object collectionValue, DelegateExecution execution) {
        //collectionValue: {"users":"1,2,3", "groups":"admin,audit"}
        LinkedHashSet<String> result = new LinkedHashSet<>();//20240826 lhm将set改成LinkedHashSet，修复会签节点顺序执行时审批人顺序错误问题
        FlowNextNodeCandidateRepository flowNextNodeCandidateRepository = SpringUtils.getBean(FlowNextNodeCandidateRepository.class);
        NodeCandidateService nodeCandidateService = SpringUtils.getBean(NodeCandidateService.class);
        String parentExecutionId = null;
        String currentExecutionId = execution.getId();
        ExecutionEntity parentProcessInstanceExecution = nodeCandidateService.getSubProcessInstanceExecution(currentExecutionId);
        if (Objects.nonNull(parentProcessInstanceExecution)){
            parentExecutionId = parentProcessInstanceExecution.getId();
        }else{
            parentExecutionId = execution.getProcessInstanceId();
        }

        List<ModelNextNodeCandidateSettings> candidateSettingsList = flowNextNodeCandidateRepository.selectByProcessInstanceIdAndNodeCodeAndParentExecutionId(execution.getProcessInstanceId()
                , execution.getCurrentActivityId(),parentExecutionId);
        if (CollectionUtils.isNotEmpty(candidateSettingsList)){
            for (ModelNextNodeCandidateSettings modelNextNodeCandidateSettings : candidateSettingsList) {
                String type = modelNextNodeCandidateSettings.getDataType();
                String value = modelNextNodeCandidateSettings.getCode();
                String name = modelNextNodeCandidateSettings.getName();
                // 流程待办时,指定候选节点候选人时保存的任务ID
                String taskId = modelNextNodeCandidateSettings.getTaskId();
                resolveCandidateUser(type,value,result,execution, taskId);
                if (!(Objects.equals("RELATIONSHIP",type) || Objects.equals("FUNCTION",type))){
                    addToParticipantInfo(value,type,name);
                }
            }
            return result;
        }

        if (collectionValue instanceof String) {
            CollectionValue cv = JSONObject.parseObject((String) collectionValue, CollectionValue.class);

            Field[] fields = cv.getClass().getDeclaredFields();
            if (Objects.nonNull(fields) && fields.length > 0){
                for (Field field : fields) {
                    try {
                        field.setAccessible(true);
                        Object valueObj = field.get(cv);
                        if(Objects.nonNull(valueObj)){
                            fillResult(result,field.getName().toUpperCase(),String.valueOf(valueObj),execution);
                        }

                    } catch (IllegalAccessException e) {
                        logger.error("getFieldValue error",e);
                        continue;
                    }
                }
            }
        }

        Map<String, Object> variableMap = execution.getVariables();
        if (Objects.nonNull(variableMap) && !variableMap.isEmpty()) {
            for (Map.Entry<String, Object> entry : variableMap.entrySet()) {
                if (execution.getCurrentActivityId().equals(entry.getKey())) {
                    String[] users = entry.getValue().toString().split(",");
                    for (String user : users) {
                        result.add(user);
                    }
                }
            }
        }
        return result;
    }


    public void fillResult(Set<String> result,String type,String expression,DelegateExecution delegateExecution){

        Object expressValue = null;
        if (ExpressionUtils.isExpression(expression)){
            ExpressionManager expressionManager = ExpressionUtils.getExpressionManager();
            expressValue = expressionManager.createExpression(expression).getValue(delegateExecution);
            if (Objects.nonNull(expressValue)){
                if (expressValue instanceof String){

                    resolveCandidateUser(type,String.valueOf(expressValue),result,delegateExecution, null);

                }else if (expressValue instanceof List){
                    List list = (List)expressValue;
                    list.stream().forEach(item -> {
                        if (item instanceof Map){
                            Map param = (Map) item;
                            String value = String.valueOf(param.get("value"));
                            resolveCandidateUser(type,value,result,delegateExecution, null);

                        }else if (item instanceof String){
                            String value = String.valueOf(item);
                            resolveCandidateUser(type,value,result,delegateExecution, null);
                        }
                    });
                }
            }
        }else{
            resolveCandidateUser(type,expression,result,delegateExecution, null);
        }
    }


    public void resolveCandidateUser(String type,String expressionValue,Set<String> result,DelegateExecution delegateExecution, String taskId) {
        String[] values = expressionValue.split(",");

        if (Objects.equals("STA",type)){
            for (String value : values) {
                result.add(value);
            }
        }else if (Objects.equals("ORG",type) || Objects.equals("ROL",type)
                || Objects.equals("GROUP",type) || Objects.equals("POSITION",type)){
            FlowNodeCandidateServiceAdapter serviceAdapter = SpringUtils.getBean(FlowNodeCandidateServiceAdapter.class);
            // 拟稿人
            String applyUser = serviceAdapter.getFlowNodeCandidateUser(delegateExecution.getProcessInstanceId());
            for (String value : values) {
                ResolveCandidateService resolveCandidateService = SpringUtils.getBean(ResolveCandidateService.class);
                List<UserInfo> userInfos = resolveCandidateService.getUserInfoByCode(type,value,applyUser);
                if (Objects.nonNull(userInfos) && !userInfos.isEmpty()){
                    for (UserInfo userInfo : userInfos) {
                        result.add(userInfo.getUserCode());
                        //保存到本地，增加下次查询的效率
                        addToParticipantInfo(userInfo.getUserCode(),"STA",userInfo.getUserName());
                    }
                }
            }

        }else if (Objects.equals("RELATIONSHIP",type) ||
                Objects.equals("FUNCTION",type)){
            FlowNodeCandidateServiceAdapter serviceAdapter = SpringUtils.getBean(FlowNodeCandidateServiceAdapter.class);
            for (String value : values) {
                try {
                    // 解析候选人列表
                    Set<UserInfo> userInfoSet = serviceAdapter.handleNextNodeCandidateUsers(type, value, delegateExecution.getProcessInstanceId(), taskId, delegateExecution.getCurrentActivityId());
                    result.addAll(this.convertFunctionResultToStr(userInfoSet));

                    resolveParticipantInfo(userInfoSet);
                } catch(Exception ex) {
                    logger.error("获取人员信息列表失败",ex);
                }
            }
        } else if (Objects.equals("USERS",type) || Objects.equals("GROUPS",type)){
            for (String value : values) {
                result.add(value);
            }
        }
    }

    private void resolveParticipantInfo(Set<UserInfo> userInfoSet) {
        if (CollectionUtils.isNotEmpty(userInfoSet)){
            userInfoSet.stream().forEach(userInfo -> {
                addToParticipantInfo(userInfo.getUserCode(),"STA",userInfo.getUserName());
            });
        }
    }

    private List<String> convertFunctionResultToStr(Set<UserInfo> userInfoSet) {
        if(CollectionUtils.isEmpty(userInfoSet)) {
            return Collections.emptyList();
        }
        // 类型转换
        List<String> strlist = userInfoSet.stream().map(userInfo -> {
            return userInfo.getUserCode();
        }).collect(Collectors.toList());

        return strlist;
    }


    private void addToParticipantInfo(String value, String type, String name) {
        try {
            FlowParticipantInfoVo flowParticipantInfo = newFlowParticipantInfo(value,type,name);
            FlowParticipantRepository flowParticipantRepository = SpringUtils.getBean(FlowParticipantRepository.class);
            flowParticipantRepository.insertParticipant(flowParticipantInfo);
        } catch (Exception e) {
            logger.error("save participantInfo error,continue");
        }
    }

    private FlowParticipantInfoVo newFlowParticipantInfo(String participantId, String participantType, String participantName) {
        FlowParticipantInfoVo flowParticipantInfo = new FlowParticipantInfoVo();
        flowParticipantInfo.setParticipantId(participantId);
        flowParticipantInfo.setParticipantType(participantType);
        flowParticipantInfo.setParticipantName(participantName);
        flowParticipantInfo.setCreateTime(new Date());
        flowParticipantInfo.setUpdateTime(new Date());
        return flowParticipantInfo;
    }


    private void insertParticipantForGroupOrRole(String[] groups) {
        FlowParticipantRepository flowParticipantRepository = SpringUtils.getBean(FlowParticipantRepository.class);
        UserRepository userRepository = SpringUtils.getBean(UserRepository.class);
        for (String group : groups) {
            FlowParticipantInfoVo flowParticipantInfo = new FlowParticipantInfoVo();
            flowParticipantInfo.setParticipantId(group);
            flowParticipantInfo.setParticipantType("ORG");
            flowParticipantInfo.setParticipantName(userRepository.getOrgNameByCode(group));
            flowParticipantInfo.setCreateTime(new Date());
            flowParticipantInfo.setUpdateTime(new Date());
            flowParticipantRepository.insertParticipant(flowParticipantInfo);
        }
    }

    private void resolveUserByOrgs(String[] orgs, List<String> result) {
        UserRepository userRepository = SpringUtils.getBean(UserRepository.class);
        for (String org : orgs) {
            List<User> users = userRepository.listUserByOrgCode(org);
            result.addAll(users.stream().map(User::getUserCode).collect(Collectors.toList()));
        }
    }

    private void resolveUserByRoles(String[] roles, List<String> result) {
        UserRepository userRepository = SpringUtils.getBean(UserRepository.class);
        for (String role : roles) {
            List<User> users = userRepository.listUserByRoleId(role);
            result.addAll(users.stream().map(User::getUserCode).collect(Collectors.toList()));
        }
    }


    static class CollectionValue {


        /**
         * 用户变量表达式
         */
        private String sta;

        /**
         * 用户组表达式
         */
        private String group;

        /**
         * 角色表达式
         */
        private String rol;

        /**
         * 岗位
         */
        private String position;

        /**
         * 组织列表
         */
        private String org;

        /**
         * 角色
         */
        private String relationship;


        private String function;



        /**
         * 存量数据，保证能正常解析流转
         */
        private String users;

        /**
         * 存量数据，保证能正常解析流转
         */
        private String groups;


        public String getSta() {
            return sta;
        }

        public void setSta(String sta) {
            this.sta = sta;
        }

        public String getGroup() {
            return group;
        }

        public void setGroup(String group) {
            this.group = group;
        }

        public String getRol() {
            return rol;
        }

        public void setRol(String rol) {
            this.rol = rol;
        }

        public String getPosition() {
            return position;
        }

        public void setPosition(String position) {
            this.position = position;
        }

        public String getOrg() {
            return org;
        }

        public void setOrg(String org) {
            this.org = org;
        }

        public String getRelationship() {
            return relationship;
        }

        public void setRelationship(String relationship) {
            this.relationship = relationship;
        }

        public String getFunction() {
            return function;
        }

        public void setFunction(String function) {
            this.function = function;
        }


        public String getUsers() {
            return users;
        }

        public void setUsers(String users) {
            this.users = users;
        }

        public String getGroups() {
            return groups;
        }

        public void setGroups(String groups) {
            this.groups = groups;
        }
    }
}
